package cn.keniykey.mc.viewlibrary.view.api;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Nullable;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;

import cn.keniykey.mc.viewlibrary.container.api.Container;
import cn.keniykey.mc.viewlibrary.container.api.ContainerClickReport;
import cn.keniykey.mc.viewlibrary.container.api.EContainerType;
import cn.keniykey.mc.viewlibrary.container.api.IContainerClickHandler;
import cn.keniykey.mc.viewlibrary.container.api.IContainerCloseHandler;
import cn.keniykey.mc.viewlibrary.container.api.IContainerOpenHandler;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionConstructor;

/*
 * 2021年10月6日 周三 1点
 * 定义：玩家观看单一界面的视图
 */
public abstract class AbFrameView {
	private static HashMap<String,HashSet<Class<? extends AbFrameView>>> hashmap_plugins_views = new HashMap<>();
	private static HashMap<Class<? extends AbFrameView>,ReflectionConstructor> hashmap_views_constructor = new HashMap<>();
	
	public static void register(Plugin plugin,Class<? extends AbFrameView> clazz_view) {
		final ReflectionConstructor constructor;
		try {
			constructor = new ReflectionConstructor(clazz_view.getDeclaredConstructor(Player.class));
		}catch(Exception ex) {
			throw new RuntimeException("cannot find the constructor for only player data(找不到单独Player数据的构建方法): " + clazz_view.getName(), ex);
		}
		final String plugin_name = plugin.getName();
		final HashSet<Class<? extends AbFrameView>> hashset_views;
		if(hashmap_plugins_views.containsKey(plugin_name)) {
			hashset_views = hashmap_plugins_views.get(plugin_name);
		}else {
			hashset_views = new HashSet<>();
			hashmap_plugins_views.put(plugin_name, hashset_views);
		}
		if(hashset_views.add(clazz_view)) hashmap_views_constructor.put(clazz_view, constructor);
	}
	public static void unRegister(Plugin plugin) {
		final String plugin_name = plugin.getName();
		if(!hashmap_plugins_views.containsKey(plugin_name)) return;
		final HashSet<Class<? extends AbFrameView>> set_views = hashmap_plugins_views.get(plugin_name);
		hashmap_plugins_views.remove(plugin_name);
		for(final Class<? extends AbFrameView> clazz : set_views) hashmap_views_constructor.remove(clazz);
		set_views.clear();
	}
	static AbFrameView newObject(Player player,Class<? extends AbFrameView> clazz_view) {
		if(!hashmap_views_constructor.containsKey(clazz_view)) throw new RuntimeException("not registered(未注册): " + clazz_view.getName());
		return (AbFrameView) hashmap_views_constructor.get(clazz_view).newInstance(player);
	}
	private void setViewState(EViewState state) {
		this.view_state = state;
		try {
			switch(state) {
			case CREATE:
				this.onCreate();
				break;
			case SHOW_BEFORE:
				this.onShowBefore();
				break;
			case SHOW:
				this.onShow();
				break;
			case HIDDEN:
				this.onHidden();
				break;
			case DESTROY:
				this.onDestroy();
				break;
			default:
			}
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	private static void changeViewState(AbFrameView view, EViewState state) {
		if(view.view_state.equals(state)) return;
		final FrameViewIntent intent = view.getIntent();
		switch(state) {
		case CREATE:
			try {
				if(null != view.content && 1 == (++view.content.view_amount)) view.content.onCreate();
			}catch(Exception ex) {
				ex.printStackTrace();
			}
			if(intent.requestcode < 1) {
				view.setViewState(state);
				break;
			}
			try {
				view.onCreateForResult(intent.requestcode);
			}catch(Exception ex) {
				ex.printStackTrace();
			}
			break;
		case SHOW_BEFORE:
			view.setViewState(state);
			break;
		case SHOW:
			if(null != view.getPluginViewContent()) {
				try {
					view.getPluginViewContent().onInvoke(view);
				}catch(Exception ex) {
					ex.printStackTrace();
				}
			}
			view.setViewState(state);
			break;
		case HIDDEN:
			if(null != view.getPluginViewContent()) {
				try {
					view.getPluginViewContent().onPause(view);
				}catch(Exception ex) {
					ex.printStackTrace();
				}
			}
			view.setViewState(state);
			break;
		case DESTROY:
			view.setViewState(state);
			if(null != view.content && 0 == (--view.content.view_amount)) {
				try {
					view.content.onDestroy();
				}catch(Exception ex) {
					ex.printStackTrace();
				}
				AbPluginViewContent.removeOfCache(view.player.getUniqueId(), view.plugin);
			}
			view.container.clearItemStacks();
			
			view.container.setIContainerOpenHandler(null);
			view.container.setIContainerClickHandler(null);
			view.container.setIContainerCloseHandler(null);
			view.content = null;
			if(null != view.intent.view_after) {
				view.intent.view_after.intent.view_before = view.intent.view_before;
				if(null != view.intent.view_before) view.intent.view_before.intent.view_after = view.intent.view_after;
				view.intent.view_before = (view.intent.view_after = null);
				view.intent = null;
			}
			break;
		default:
		}
	}
	
	public enum EViewState {
		UNKNOWN(),
		CREATE(),
		SHOW_BEFORE(),
		SHOW(),
		HIDDEN(),
		DESTROY();
	}
	
	private final Plugin plugin;
	private final Player player;
	private Container container;
	private final HashMap<Integer, IViewClickHandler> hashmap_icons_clickhandler = new HashMap<>();
	private @Nullable IViewClickHandler clickhandler_backpack, clickhandler_outside;
	
	private FrameViewIntent intent;
	private @Nullable AbPluginViewContent content;
	private boolean allow_backpack_curitem_empty = false;
	
	private volatile EViewState view_state = EViewState.UNKNOWN;
	private volatile EViewState view_state_tager = EViewState.UNKNOWN;
	
	/**
	 * 具体化实现类的构建方法参数请仅保留Player
	 * Plugin需自行传递实现本类的插件
	 */
	protected AbFrameView(Plugin plugin1,Player player1) {
		this.plugin = plugin1;
		this.player = player1;
	}
	
	public final Plugin getPlugin() {
		return this.plugin;
	}
	public final Player getPlayer() {
		return this.player;
	}
	@Nullable
	protected final AbPluginViewContent getPluginViewContent() {
		return this.content;
	}
	protected final void refreshView() {
		this.checkDestroy();
		if(!EViewState.SHOW.equals(this.view_state)) return;
		this.container.refreshItemStack();
	}
	protected final void refreshIcon(int slot) {
		this.checkDestroy();
		if(!EViewState.SHOW.equals(this.view_state)) return;
		this.container.refreshItemStack(slot);
	}
	protected final void setBackPackClickHandler(@Nullable IViewClickHandler handler,boolean allow_click_empty) {
		this.checkDestroy();
		this.clickhandler_backpack = handler;
		this.allow_backpack_curitem_empty = allow_click_empty;
	}
	protected final void setOutSizeClickHandler(@Nullable IViewClickHandler handler) {
		this.checkDestroy();
		this.clickhandler_outside = handler;
	}
	protected final void setIconClick(int slot,@Nullable IViewClickHandler handler) {
		this.checkDestroy();
		if(null == handler) {
			this.hashmap_icons_clickhandler.remove(slot);
			return;
		}
		this.hashmap_icons_clickhandler.put(slot, handler);
	}
	protected final void setIconClick(int slot,@Nullable ItemStack item,@Nullable IViewClickHandler handler) {
//		this.setIcon(slot, item);
		this.checkDestroy();
		if(null == this.container) throw new RuntimeException("Please set view type first（请先决定界面类型）");
		this.container.setItemStack(slot, item, EViewState.SHOW.equals(this.view_state));
//		this.setIconClick(slot, handler);
		if(null == handler) {
			this.hashmap_icons_clickhandler.remove(slot);
			return;
		}
		this.hashmap_icons_clickhandler.put(slot, handler);
	}
	protected final void clearIconsClick() {
		this.checkDestroy();
		this.hashmap_icons_clickhandler.clear();
	}
	protected final void clearIconsClick(int begin,int end) {
		this.checkDestroy();
		for(int index = begin; index < end; index++) this.hashmap_icons_clickhandler.remove(index);
	}
	protected final void setIcon(int slot,@Nullable ItemStack item) {
		this.checkDestroy();
		if(null == this.container) throw new RuntimeException("Please set view type first（请先决定界面类型）");
		this.container.setItemStack(slot, item, EViewState.SHOW.equals(this.view_state));
	}
	protected final void setIcons(Map<Integer,ItemStack> map_items) {
		this.checkDestroy();
		if(null == this.container) throw new RuntimeException("Please set view type first（请先决定界面类型）");
		this.container.clearItemStacks();
		final boolean show = EViewState.SHOW.equals(this.view_state);
		for(final Entry<Integer, ItemStack> entry : map_items.entrySet()) this.container.setItemStack(entry.getKey(),
				entry.getValue(), show);
	}
	protected final void clearIcons() {
		this.checkDestroy();
		if(null == this.container) throw new RuntimeException("Please set view type first（请先决定界面类型）");
		this.container.clearItemStacks();
	}
	protected final void clearIcons(int begin,int end) {
		this.checkDestroy();
		if(null == this.container) throw new RuntimeException("Please set view type first（请先决定界面类型）");
		for(int index = begin; index < end; index++) this.container.setItemStack(index, null, false);
		if(EViewState.SHOW.equals(this.view_state)) this.container.refreshItemStack();
	}
	/**
	 * 改变Title与View类型均重新打开界面视图
	 * 因此本操作包含，刷新标题，刷新界面类型，刷新物品显示
	 */
	protected final void setTitle(String title1) {
		this.checkDestroy();
		if(null == this.container) throw new RuntimeException("Please set view type first（请先决定界面类型）");
		this.container.setTitle(title1);
	}
	/**
	 * 改变Title与View类型均重新打开界面视图
	 * 因此本操作包含，刷新标题，刷新界面类型，刷新物品显示
	 */
	protected final void setViewType(EContainerType type) {
		this.checkDestroy();
		if(null != this.container) {
			this.container.setEContainergetType(type);
			return;
		}
		this.container = Container.createContainer(type);
	}
	private final class viewClickReportRunnable implements Runnable{
		private final Player player;
		private final IViewClickHandler handler;
		private final ViewClickReport report;
		viewClickReportRunnable(Player player1,IViewClickHandler handler1,ViewClickReport report1) {
			this.player = player1; this.handler = handler1; this.report = report1;
		}

		@Override
		public void run() {
			if(!EViewState.SHOW.equals(AbFrameView.this.view_state)) return;
			try {
				this.handler.onClick(this.player, AbFrameView.this, this.report);
			}catch(Exception ex) {
				ex.printStackTrace();
			}
		}
	}
	
	protected void onCreate() {} //创建视图
	protected void onCreateForResult(int requestcode) {}
	protected void onShowBefore() {} //视图显示给玩家看之前
	protected void onShow() {} //视图显示给玩家之后
	protected void onHidden() {} //视图不显示给玩家看
	/**
	 * 视图彻底销毁关闭
	 * 注意：调用完此方法后，容器将清空
	 */
	protected void onDestroy() {} //视图彻底销毁关闭
	
	protected void onViewResult(int requestcode, int resultcode,@Nullable FrameViewBundle bundle) {}
	
	protected final FrameViewIntent getIntent() {
		return this.intent;
	}
	@Nullable
	protected final Container getContainer() {
		return this.container;
	}
	protected final void setResult(int resultcode,@Nullable FrameViewBundle bundle) {
		this.checkDestroy();
		if(null == this.intent) throw new NullPointerException("The Intent is not set for show(show时未设置Intent)");
		if(this.intent.requestcode <= 0) throw new IllegalStateException("This View is not a result View(此View不为反馈View");
		if(resultcode <= 0) throw new RuntimeException("Technical specification: The number must be greater than 0(技术规范：编号必须大于0)");
		final AbFrameView view = this.intent.view_before;
		if(null == view) return;
		synchronized (view.view_state_tager) {
			if(EViewState.DESTROY.equals(view.view_state_tager)) return;
		}
		view.onViewResult(this.intent.requestcode, resultcode, bundle);
	}
	public final boolean isDestroy() {
		return EViewState.DESTROY.equals(this.view_state);
	}
	private final void checkDestroy() {
		if(EViewState.DESTROY.equals(this.view_state)) {
			throw new IllegalStateException("The View was destroy(视图已销毁)");
		}
		if(EViewState.DESTROY.equals(this.view_state_tager)) {
			throw new IllegalStateException("The View is in destroy(视图销毁中)");
		}
	}
	public void show() {
		this.checkDestroy();
		if(!EViewState.HIDDEN.equals(this.view_state)) {
			throw new IllegalStateException("It is not in hidden state(不在隐藏状态)");
		}
		synchronized (this.view_state_tager) {
			if(EViewState.SHOW.equals(this.view_state_tager)) return;
		}
		if(null != this.intent.view_after) throw new IllegalStateException("This View is not at the front desk(这个View并不是最前台的)");
		synchronized (this.view_state_tager) {
			this.view_state_tager = EViewState.SHOW;
		}
		Bukkit.getScheduler().runTaskLaterAsynchronously(this.plugin, ()->{
			this.openContainer();
		}, 10l);
	}
	public void hidden() {
		this.checkDestroy();
		if(!EViewState.SHOW.equals(this.view_state)) {
			throw new IllegalStateException("It is not in show state(不在显示状态)");
		}
		synchronized (this.view_state_tager) {
			if(EViewState.HIDDEN.equals(this.view_state_tager)) return;
			this.view_state_tager = EViewState.HIDDEN;
		}
		this.container.close(this.player, true, true);
	}
	public final void finish() {
		if(EViewState.DESTROY.equals(this.view_state)) {
			throw new IllegalStateException("It have been destroyed(已经被销毁)");
		}
		synchronized (this.view_state_tager) {
			if(EViewState.DESTROY.equals(this.view_state_tager)) return;
			this.view_state_tager = EViewState.DESTROY;
		}
		if(null != this.container) this.container.close(this.player, EViewState.SHOW.equals(this.view_state), true);
	}
	public final void finishAll() {
		if(EViewState.DESTROY.equals(this.view_state)) {
			throw new IllegalStateException("It have been destroyed(已经被销毁)");
		}
		synchronized (this.view_state_tager) {
			if(EViewState.DESTROY.equals(this.view_state_tager)) return;
			this.view_state_tager = EViewState.DESTROY;
		}
		AbFrameView view = this;
		AbFrameView front_desk_view = null;
		do {
			front_desk_view = view;
			view = front_desk_view.intent.view_after;
		}while(null != view); //以此向下遍历，获取最后View，待显示或准备显示的View
		
		synchronized (front_desk_view.view_state_tager) {
			front_desk_view.view_state_tager = EViewState.DESTROY;
		}
		view = front_desk_view;
		while(null != (view = view.intent.view_before)) { //以此向上遍历，把每个View都设为将销毁
			synchronized (view.view_state_tager) {
				view.view_state_tager = EViewState.DESTROY;
			}
		}
		if(null != front_desk_view.container) {
			front_desk_view.container.close(this.player, EViewState.SHOW.equals(front_desk_view.view_state), false);
		}else {
			AbFrameView.changeViewState(front_desk_view, EViewState.DESTROY);
			if(null != front_desk_view.intent && null != front_desk_view.intent.view_before) {
				view = front_desk_view.intent.view_before;
				if(null != view.container) view.container.close(this.player, EViewState.SHOW.equals(view.view_state), false);
			}
		}
	}
	public static void showView(Player player,FrameViewIntent intent) {
		if(Container.hasViewingContainer(player)) {
			throw new IllegalStateException("Please use the dynamic method of AbFrameView(请使用AbFrameView的动态方法)");
		}
		if(null != intent.view_after || null != intent.view_before) throw new RuntimeException("This Intent has already been passed(传递的Intent不是新New的)");
		final AbFrameView newview = newObject(player, intent.clazz_targetview);
		newview.content = AbPluginViewContent.newObject(player, newview.getPlugin());
		newview.intent = intent;
		synchronized (newview.view_state_tager) {
			newview.view_state_tager = EViewState.SHOW;
		}
		Bukkit.getScheduler().runTaskLaterAsynchronously(newview.getPlugin(), ()-> {
			AbFrameView.changeViewState(newview, EViewState.CREATE);
			newview.openContainer();
		}, 10l);
	}
	protected final void showViewForResult(FrameViewIntent intent,int requestcode) {
		if(requestcode < 1) throw new RuntimeException("Technical specification: The number must be greater than 0(技术规范：编号必须大于0)");
		intent.requestcode = requestcode;
		this.showView(intent);
	}
	public final boolean showView(FrameViewIntent intent) {
		if(null != intent.view_after) throw new RuntimeException("This Intent has already been passed(传递的Intent不是新New的)");
		synchronized (this.view_state_tager) {
			if(EViewState.DESTROY.equals(this.view_state_tager)) return false;
		}
		if(null != this.intent.view_after) throw new IllegalStateException("This View is not at the front desk(这个View并不是最前台的)");
		if(this.intent.requestcode > 0) throw new IllegalStateException("This View is a feedback View and should not show other new views(此View为反馈View，不应再show其它新View)");
		final AbFrameView newview = newObject(this.player, intent.clazz_targetview);
		newview.content = AbPluginViewContent.newObject(this.player, newview.getPlugin());
		newview.intent = intent;
		intent.view_before = this;
		this.intent.view_after = newview;
		synchronized (newview.view_state_tager) {
			newview.view_state_tager = EViewState.SHOW;
		}
		synchronized (this.view_state_tager) {
			this.view_state_tager = EViewState.HIDDEN;
		}
		Bukkit.getScheduler().runTaskLaterAsynchronously(newview.getPlugin(), ()-> {
			if(this.isDestroy()) return;
			AbFrameView.changeViewState(this, EViewState.HIDDEN);
			AbFrameView.changeViewState(newview, EViewState.CREATE);
			newview.openContainer();
		}, 10l);
		return true;
	}
	private void openContainer() {
		if(this.isDestroy()) return;
		if(null == this.container.getIContainerClickHandler()) {
			this.container.setIContainerClickHandler(new IContainerClickHandler() {
				@Override
				public void onContainerClickResponse(Player player, ContainerClickReport report) {
					if(!EViewState.SHOW.equals(AbFrameView.this.view_state)) return;
					if(report.getClickSlot() < 0) {
						if(null == clickhandler_outside) return;
						final Runnable run = new viewClickReportRunnable(player, clickhandler_outside, new ViewClickReport(report));
						if(clickhandler_outside.isAsynchronous()) {
							Bukkit.getScheduler().runTaskAsynchronously(plugin, run);
						}else {
							Bukkit.getScheduler().runTask(plugin, run);
						}
						return;
					}
					if(report.isClickContainer()) {
						if(!hashmap_icons_clickhandler.containsKey(report.getClickSlot())) return;
						final IViewClickHandler handler = hashmap_icons_clickhandler.get(report.getClickSlot());
						final Runnable run = new viewClickReportRunnable(player, handler, new ViewClickReport(report));
						if(handler.isAsynchronous()) {
							Bukkit.getScheduler().runTaskAsynchronously(plugin, run);
						}else {
							Bukkit.getScheduler().runTask(plugin, run);
						}
						return;
					}
					if(report.isClickPlayerBackpack()) {
						if(null == clickhandler_backpack) return;
						final ItemStack curitem = report.getCursorItemStack();
						if(!allow_backpack_curitem_empty && (null == curitem|| curitem.getType().isAir())) return;
						final Runnable run = new viewClickReportRunnable(player, clickhandler_backpack, new ViewClickReport(report));
						if(clickhandler_backpack.isAsynchronous()) {
							Bukkit.getScheduler().runTaskAsynchronously(plugin, run);
						}else {
							Bukkit.getScheduler().runTask(plugin, run);
						}
						return;
					}
				}
			});
		}
		if(null == this.container.getIContainerOpenHandler()) {
			this.container.setIContainerOpenHandler(new IContainerOpenHandler() {	
				@Override
				public void onContainerOpenResponse(Player player, Container container) {
					AbFrameView.changeViewState(AbFrameView.this, EViewState.SHOW);
				}
			});
		}
		if(null == this.container.getIContainerCloseHandler()) {
			this.container.setIContainerCloseHandler(new IContainerCloseHandler() {
				@Override
				public void onContainerCloseResponse(Player player, Container container, boolean initiative) {
					if(EViewState.HIDDEN.equals(AbFrameView.this.view_state)) {
						//在后台模式关闭情况：已调用showView时finish
						AbFrameView.changeViewState(AbFrameView.this, EViewState.DESTROY);
						return;
					}
					if(EViewState.SHOW.equals(AbFrameView.this.view_state)) {
						//在显示模式下触发关闭情况：退出游戏，自行ESC关闭，被InventoryOpen，触发式finish
						//其中由玩家发起的initiative为false,finsh发起的为true
						synchronized (AbFrameView.this.view_state_tager) {
							if(EViewState.HIDDEN.equals(AbFrameView.this.view_state_tager)) {
								AbFrameView.changeViewState(AbFrameView.this, EViewState.HIDDEN);
								return;
							}
						}
//						AbFrameView.setViewState(AbFrameView.this, EViewState.DESTROY);
						if(!initiative || !player.isOnline() ||
								!player.getOpenInventory().getTopInventory().getType().equals(InventoryType.CRAFTING)) {
							AbFrameView.this.intent.view_after = null;
							AbFrameView lastview = AbFrameView.this;
							AbFrameView view;
							do {
								view = lastview.intent.view_before;
								lastview.intent.view_before = null;
								if(null != view) view.intent.view_after = null;
								if(!EViewState.DESTROY.equals(lastview.view_state)) AbFrameView.changeViewState(lastview,EViewState.HIDDEN);
								AbFrameView.changeViewState(lastview, EViewState.DESTROY);
								lastview.intent = null;
								lastview = view;
							}while(null != view);
//							AbFrameView lastview = AbFrameView.this;
//							AbFrameView view = lastview.intent.view_before;
//							while(null != view) {
//								if(!EViewState.DESTROY.equals(view.view_state)) AbFrameView.setViewState(view,EViewState.HIDDEN);
//								AbFrameView.setViewState(view,EViewState.DESTROY);
//								lastview = view;
//								view = lastview.intent.view_before;
//								lastview.intent.view_before = (lastview.intent.view_after = null);
//								lastview.intent = null;
//							}
//							lastview.intent = null;
							return;
						}
						AbFrameView.changeViewState(AbFrameView.this, EViewState.DESTROY);
						if(null == AbFrameView.this.intent) return;
						final AbFrameView view_final = AbFrameView.this.intent.view_before;
						AbFrameView.this.intent.view_before = null;
						AbFrameView.this.intent = null;
						if(null != view_final) { //当关闭时，若有隐藏，重新打开
							synchronized (view_final.view_state_tager) {
								view_final.view_state_tager = EViewState.SHOW;
							}
							view_final.intent.view_after = null;
							Bukkit.getScheduler().runTaskLaterAsynchronously(view_final.plugin, ()->{
								view_final.openContainer();
							}, 10l);
						}
						return;
					}
				}
			});
		}
		synchronized (this.view_state_tager) {
			if(!EViewState.SHOW.equals(this.view_state_tager)) return;
		}
		AbFrameView.changeViewState(this, EViewState.SHOW_BEFORE);
		synchronized (this.view_state_tager) {
			if(EViewState.SHOW.equals(this.view_state_tager)) this.container.open(this.player);
		}
	}
}